home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr27 / gs26.zip / BDFTOPS.PS < prev    next >
Text File  |  1993-01-22  |  24KB  |  757 lines

  1. %    Copyright (C) 1990, 1991 Aladdin Enterprises.  All rights reserved.
  2. %    Distributed by Free Software Foundation, Inc.
  3. %
  4. % This file is part of Ghostscript.
  5. %
  6. % Ghostscript is distributed in the hope that it will be useful, but
  7. % WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  8. % to anyone for the consequences of using it or for whether it serves any
  9. % particular purpose or works at all, unless he says so in writing.  Refer
  10. % to the Ghostscript General Public License for full details.
  11. %
  12. % Everyone is granted permission to copy, modify and redistribute
  13. % Ghostscript, but only under the conditions described in the Ghostscript
  14. % General Public License.  A copy of this license is supposed to have been
  15. % given to you along with Ghostscript so you can know your rights and
  16. % responsibilities.  It should be in a file named COPYING.  Among other
  17. % things, the copyright notice and this notice must be preserved on all
  18. % copies.
  19.  
  20. % bdftops.ps
  21. % Convert a BDF file (possibly with (an) associated AFM file(s))
  22. % to a Ghostscript font.
  23.  
  24. % Ghostscript fonts are in the same format as Adobe Type 1 fonts,
  25. % except that they do not use eexec encryption.
  26. % See gs_fonts.ps for more information.
  27.  
  28. /envBDF 120 dict def
  29. envBDF begin
  30.  
  31. % "Import" the image-to-path package.
  32. % This also brings in the Type 1 opcodes (type1ops.ps).
  33.    (impath.ps) run
  34.  
  35. % "Import" the font-writing package.
  36.    (wrfont.ps) run
  37.    /encrypt_CharStrings false def
  38.  
  39. % Invert the StandardEncoding vector.
  40.    256 dict dup begin
  41.    0 1 255 { dup StandardEncoding exch get exch def } for
  42.    end /decoding exch def
  43.  
  44. % Define the dictionary equivalent of ].
  45.    /dicttomark
  46.     { counttomark 2 idiv dup dict begin
  47.        { def } repeat
  48.       pop currentdict end 
  49.     } bind def
  50.  
  51. % Define the properties copied to FontInfo.
  52.    mark
  53.      (COPYRIGHT) /Notice
  54.      (FAMILY_NAME) /FamilyName
  55.      (FULL_NAME) /FullName
  56.      (WEIGHT_NAME) /Weight
  57.    dicttomark /properties exch def
  58.  
  59. % Define the character sequences used to fill in some undefined entries
  60. % in the standard encoding.
  61.    mark
  62.      (AE) [/A /E]
  63.      (OE) [/O /E]
  64.      (acute) [/quoteright]
  65.      (ae) [/a /e]
  66.      (bullet) [/asterisk]
  67.      (cedilla) [/comma]
  68.      (circumflex) [/asciicircum]
  69.      (dieresis) [/quotedbl]
  70.      (dotlessi) [/i]
  71.      (ellipsis) [/period /period /period]
  72.      (emdash) [/hyphen /hyphen /hyphen]
  73.      (endash) [/hyphen /hyphen]
  74.      (exclamdown) [/exclam]
  75.      (fi) [/f /i]
  76.      (fl) [/f /l]
  77.      (florin) [/f]
  78.      (fraction) [/slash]
  79.      (germandbls) [/s /s]
  80.      (grave) [/quoteleft]
  81.      (guillemotleft) [/less /less]
  82.      (guillemotright) [/greater /greater]
  83.      (guilsinglleft) [/less]
  84.      (guilsinglright) [/greater]
  85.      (hungarumlaut) [/quotedbl]
  86.      (oe) [/o /e]
  87.      (periodcentered) [/asterisk]
  88.      (questiondown) [/question]
  89.      (quotedblbase) [/comma /comma]
  90.      (quotedblleft) [/quotedbl]
  91.      (quotedblright) [/quotedbl]
  92.      (quotesinglbase) [/comma]
  93.      (quotesingle) [/quoteright]
  94.      (tilde) [/asciitilde]
  95.    dicttomark /composites exch def
  96.  
  97. % Note the characters that must be defined as subroutines.
  98.    96 dict begin
  99.      0 composites
  100.       { exch pop
  101.          { dup currentdict exch known
  102.         { pop }
  103.         { 1 index def 1 add }
  104.        ifelse
  105.      }
  106.     forall
  107.       }
  108.      forall pop
  109.      currentdict
  110.    end /subrchars exch def
  111.  
  112. % Define the overstruck characters that can be synthesized with seac.
  113.    mark
  114.     [ /Aacute /Acircumflex /Adieresis /Agrave /Aring /Atilde
  115.       /Ccedilla
  116.       /Eacute /Ecircumflex /Edieresis /Egrave
  117.       /Iacute /Icircumflex /Idieresis /Igrave
  118.       /Lslash
  119.       /Ntilde
  120.       /Oacute /Ocircumflex /Odieresis /Ograve /Otilde
  121.       /Scaron
  122.       /Uacute /Ucircumflex /Udieresis /Ugrave
  123.       /Yacute /Ydieresis
  124.       /Zcaron
  125.       /aacute /acircumflex /adieresis /agrave /aring /atilde
  126.       /ccedilla
  127.       /eacute /ecircumflex /edieresis /egrave
  128.       /iacute /icircumflex /idieresis /igrave
  129.       /lslash
  130.       /ntilde
  131.       /oacute /ocircumflex /odieresis /ograve /otilde
  132.       /scaron
  133.       /uacute /ucircumflex /udieresis /ugrave
  134.       /yacute /ydieresis
  135.       /zcaron
  136.     ]
  137.     { dup dup length string cvs
  138.       [ exch dup 0 1 getinterval
  139.         exch dup length 1 sub 1 exch getinterval
  140.       ]
  141.     } forall
  142.      /cent [/c /slash]
  143.      /daggerdbl [/bar /equal]
  144.      /divide [/colon /hyphen]
  145.      /sterling [/L /hyphen]
  146.      /yen [/Y /equal]
  147.    dicttomark /accentedchars exch def
  148.  
  149. % ------ BDF file parsing utilities ------ %
  150.  
  151. % Define a buffer for reading the BDF file.
  152.    /buffer 400 string def
  153.  
  154. % Read a line from the BDF file into the buffer.
  155. % Define /keyword as the first word on the line.
  156. % Define /args as the remainder of the line.
  157. % If the keyword is equal to commentword, skip the line.
  158. % (If commentword is equal to a space, never skip.)
  159.    /nextline
  160.     { bdfile buffer readline not
  161.        { (Premature EOF\n) print stop } if
  162.       ( ) search
  163.        { /keyword exch def pop }
  164.        { /keyword exch def () }
  165.       ifelse
  166.       /args exch def
  167.       keyword commentword eq { nextline } if
  168.     } bind def
  169.  
  170. % Get a word argument from args.  We do *not* copy the string.
  171.    /warg        % warg -> string
  172.     { args ( ) search
  173.        { exch pop exch }
  174.        { () }
  175.       ifelse  /args exch def
  176.     } bind def
  177.  
  178. % Get an integer argument from args.
  179.    /iarg        % iarg -> int
  180.     { warg cvi
  181.     } bind def
  182.  
  183. % Get a numeric argument from args.
  184.    /narg        % narg -> int|real
  185.     { warg cvr
  186.       dup dup cvi eq { cvi } if
  187.     } bind def
  188.  
  189. % Convert the remainder of args into a string.
  190.    /remarg        % remarg -> string
  191.     { args copystring
  192.     } bind def
  193.  
  194. % Get a string argument that occupies the remainder of args.
  195.    /sarg        % sarg -> string
  196.     { args (") anchorsearch
  197.        { pop /args exch def } { pop } ifelse
  198.       args args length 1 sub get (") 0 get eq
  199.        { args 0 args length 1 sub getinterval /args exch def } if
  200.       args copystring
  201.     } bind def
  202.  
  203. % Check that the keyword is the expected one.
  204.    /checkline        % (EXPECTED-KEYWORD) checkline ->
  205.     { dup keyword ne
  206.        { (Expected ) print =
  207.          (Line=) print keyword print ( ) print args print (\n) print stop
  208.        } if
  209.       pop
  210.     } bind def
  211.  
  212. % Read a line and check its keyword.
  213.    /getline        % (EXPECTED-KEYWORD) getline ->
  214.     { nextline checkline
  215.     } bind def
  216.  
  217. % Find the first/last non-zero bit of a non-zero byte.
  218.    /fnzb
  219.     { 0 { exch dup 128 ge { pop exit } { dup add exch 1 add } ifelse }
  220.       loop
  221.     } bind def
  222.    /lnzb
  223.     { 7 { exch dup 1 and 0 ne { pop exit } { -1 bitshift exch 1 sub } ifelse }
  224.       loop
  225.     } bind def
  226.  
  227. % ------ Type 1 encoding utilities ------ %
  228.  
  229. % Parse the side bearing and width information that begins a CharString.
  230. % Arguments: charstring.  Result: mark sbx wx substring *or*
  231. % mark sbx sby wx wy substring.
  232.    /parsesbw
  233.     { mark exch lenIV
  234.        {        % stack: mark ... string dropcount
  235.          dup 2 index length exch sub getinterval
  236.      dup 0 get dup 32 lt { pop exit } if
  237.      dup 246 le
  238.       { 139 sub exch 1 }
  239.       { dup 250 le
  240.          { 247 sub 8 bitshift 108 add 1 index 1 get add exch 2 }
  241.          { dup 254 le
  242.             { 251 sub 8 bitshift 108 add 1 index 1 get add neg exch 2 }
  243.         { pop dup 1 get 128 xor 128 sub
  244.           8 bitshift 1 index 2 get add
  245.           8 bitshift 1 index 3 get add
  246.           8 bitshift 1 index 4 get add exch 5
  247.         } ifelse
  248.          } ifelse
  249.       } ifelse
  250.        } loop
  251.     } bind def 
  252.  
  253. % Find the side bearing and width information that begins a CharString.
  254. % Arguments: charstring.  Result: charstring sizethroughsbw.
  255.    /findsbw
  256.     { dup parsesbw counttomark 1 add 1 roll cleartomark skipsbw
  257.     } bind def
  258.    /skipsbw        % charstring sbwprefix -> sizethroughsbw
  259.     { length 1 index length exch sub
  260.       2 copy get 12 eq { 2 } { 1 } ifelse add
  261.     } bind def
  262.  
  263. % Encode a number, and append it to a string.
  264. % Arguments: str num.  Result: newstr.
  265.    /concatnum
  266.     { dup dup -107 ge exch 107 le and
  267.        { 139 add 1 string dup 0 3 index put }
  268.        { dup dup -1131 ge exch 1131 le and
  269.           { dup 0 ge { 16#f694 } { neg 16#fa94 } ifelse add
  270.         2 string dup 0 3 index -8 bitshift put
  271.         dup 1 3 index 255 and put
  272.       }
  273.       { 5 string dup 0 255 put exch
  274.         2 copy 1 exch -24 bitshift 255 and put
  275.         2 copy 2 exch -16 bitshift 255 and put
  276.         2 copy 3 exch -8 bitshift 255 and put
  277.         2 copy 4 exch 255 and put
  278.         exch
  279.       }
  280.      ifelse
  281.        }
  282.       ifelse exch pop concatstrings
  283.     } bind def
  284.  
  285. % Encode a subroutine call for a given character, appending it to a string.
  286. % Arguments: str subrindex.  Result: newstr.
  287.    /concatcall
  288.     { () exch concatnum
  289.       s_callsubr concatstrings concatstrings
  290.     } bind def
  291.  
  292. % ------ Point arithmetic utilities ------ %
  293.  
  294.    /ptadd { exch 4 -1 roll add 3 1 roll add } bind def
  295.  
  296.    /ptexch { 4 2 roll } bind def
  297.  
  298.    /ptneg { neg exch neg exch } bind def
  299.  
  300.    /ptsub { ptneg ptadd } bind def
  301.  
  302. % ------ The main program ------ %
  303.  
  304.    /readBDF        % infilename outfilename fontname encodingname
  305.             %   uniqueID readBDF -> font
  306.     { /uniqueID exch def
  307.       /encoding exch def
  308.       /fontname exch def
  309.       /psname exch def
  310.       /bdfname exch def
  311.       gsave        % so we can set the CTM to the font matrix
  312.  
  313. %  Open the input files.  We don't open the output file until
  314. %  we've done a minimal validity check on the input.
  315.       bdfname (r) file /bdfile exch def
  316.       /commentword ( ) def
  317.  
  318. %  Check for the STARTFONT.
  319.       (STARTFONT) getline
  320.       args (2.1) ne { (Not version 2.1\n) print stop } if
  321.  
  322. %  Initialize the font.
  323.       /Font 20 dict def
  324.       Font begin
  325.       /FontName fontname def
  326.       /PaintType 0 def
  327.       /FontType 1 def
  328.       /UniqueID uniqueID def
  329.       /Encoding encoding cvx exec def
  330.       /FontInfo 20 dict def
  331.       /Private 20 dict def
  332.       currentdict end currentdict end
  333.       exch begin begin        % insert font above environment
  334.  
  335. %  Initialize the Private dictionary in the font.
  336.       Private begin
  337.       /-! {string currentfile exch readhexstring pop} readonly def
  338.       /-| {string currentfile exch readstring pop} readonly def
  339.       /|- {readonly def} readonly def
  340.       /| {readonly put} readonly def
  341.       /BlueValues [] def
  342.       /lenIV lenIV def
  343.       /MinFeature {16 16} def
  344.       /password 5839 def
  345.       /UniqueID uniqueID def
  346.       end        % Private
  347.  
  348. %  Now open the output file.
  349.       psname (w) file /psfile exch def
  350.  
  351. %  Put out a header compatible with the Adobe "standard".
  352.       (%!FontType1-1.0: ) ws fontname wt (000.000) wl
  353.       (% This is a font description converted from ) ws
  354.         bdfname wl
  355.       (%   by bdftops running on revision ) ws
  356.       revision wt (of (a) ) ws
  357.       statusdict /product get ws (.) wl
  358.  
  359. %  Copy the initial comments, up to FONT.
  360.       true
  361.       { nextline
  362.         keyword (COMMENT) ne {exit} if
  363.     { (% Here are the initial comments from the BDF file:\n%) wl
  364.     } if false
  365.     (%) ws remarg wl
  366.       } loop pop
  367.       /commentword (COMMENT) def    % do skip comments from now on
  368.  
  369. %  Read and process the FONT, SIZE, and FONTBOUNDINGBOX.
  370.       % If we cared about FONT, we'd use it here.  If the BDF files
  371.       % from MIT had PostScript names rather than X names, we would
  372.       % care; but what's there is unusable, so we discard FONT.
  373.       (FONT) checkline
  374.       (SIZE) getline
  375.         /pointsize iarg def   /xres iarg def   /yres iarg def
  376.       (FONTBOUNDINGBOX) getline
  377.         /fbbw iarg def   /fbbh iarg def   /fbbxo iarg def   /fbbyo iarg def
  378.     /fraster fbbw 7 add 8 idiv def
  379.       nextline
  380.  
  381. % Allocate the buffers for the bitmap and the outline,
  382. % according to the font bounding box.
  383.       /bits fraster fbbh mul 200 max 65535 min string def
  384.       /outline bits length 6 mul 65535 min string def
  385.  
  386. %  The Type 1 font machinery really only works with a 1000 unit
  387. %  character coordinate system.  Set this up here.
  388.  
  389. % Compute the factor to make the X entry in the FontMatrix
  390. % come out at exactly 0.001.
  391.       /fontscale   72 pointsize div xres div 1000 mul   def
  392.       Font /FontBBox
  393.        [ fbbxo fontscale mul
  394.      fbbyo fontscale mul
  395.      fbbxo fbbw add fontscale mul
  396.      fbbyo fbbh add fontscale mul
  397.        ] cvx readonly
  398.       put
  399.  
  400. %  Read and process the properties.  We only care about a few of them.
  401.       keyword (STARTPROPERTIES) eq
  402.        { iarg
  403.           { nextline
  404.         properties keyword known
  405.          { FontInfo properties keyword get sarg readonly put
  406.          } if
  407.       } repeat
  408.          (ENDPROPERTIES) getline
  409.      nextline
  410.        } if
  411.  
  412. %  Compute and set the FontMatrix.
  413.       Font /FontMatrix
  414.        [ 0.001 0 0 0.001 xres mul yres div 0 0 ] readonly
  415.       dup setmatrix put
  416.  
  417. %  Read and process the header for the bitmaps.
  418.       (CHARS) checkline
  419.         /ccount iarg def
  420.  
  421. %  Initialize the character subroutine table and the CharStrings dictionary.
  422.       /subrs subrchars length array def
  423.       /subrsbw subrchars length array def
  424.       /subrcount 0 def
  425.       /charstrings ccount composites length add
  426.         accentedchars length add 1 add dict def        % 1 add for .notdef
  427.       /isfixedwidth true def
  428.       /fixedwidth null def
  429.  
  430. %  Read and process the bitmap data.  This reads the remainder of the file.
  431.       ccount -1 1
  432.        { (STARTCHAR) getline
  433.            /charname remarg def
  434.      (/) print charname print
  435.        10 mod 1 eq { (\n) print flush } if
  436.      (ENCODING) getline        % Ignore, assume StandardEncoding
  437.      (SWIDTH) getline
  438.        /swx iarg pointsize mul 1000 div xres mul 72 div def
  439.        /swy iarg pointsize mul 1000 div xres mul 72 div def
  440.      (DWIDTH) getline        % Ignore, use SWIDTH instead
  441.      (BBX) getline
  442.        /bbw iarg def  /bbh iarg def  /bbox iarg def  /bboy iarg def
  443.      nextline
  444.      keyword (ATTRIBUTES) eq
  445.       { nextline
  446.       } if
  447.      (BITMAP) checkline
  448.  
  449. %  Read the bits for this character.
  450.      bbw 7 add 8 idiv /raster exch def
  451. % The bitmap handed to type1imagepath must have the correct height,
  452. % because type1imagepath uses this to compute the scale factor,
  453. % so we have to clear the unused parts of it.
  454.      bits dup 0 1 raster fbbh mul 1 sub
  455.       { 0 put dup } for
  456.      pop pop
  457.      raster fbbh bbh sub mul   raster   raster fbbh 1 sub mul
  458.       { bits exch raster getinterval
  459.         bdfile buffer readline not
  460.          { (EOF in bitmap\n) print stop } if
  461.         % stack has <bits.interval> <buffer.interval>
  462.         0 () /SubFileDecode filter
  463.         exch 2 copy readhexstring pop pop pop closefile
  464.       } for
  465.      (ENDCHAR) getline
  466.  
  467. %  Compute the font entry, converting the bitmap to an outline.
  468.      bits 0 raster fbbh mul getinterval    % the bitmap image
  469.      bbw   fbbh                % bitmap width & height
  470.      swx   swy                % width x & y
  471.      bbox neg   bboy neg            % origin x & y
  472.          % Account for lenIV when converting the outline.
  473.      outline  lenIV  outline length lenIV sub  getinterval
  474.      type1imagepath
  475.      length lenIV add
  476.      outline exch 0 exch getinterval
  477.  
  478. % Check for a fixed width font.
  479.      isfixedwidth
  480.       { fixedwidth null eq
  481.          { /fixedwidth swx def }
  482.          { fixedwidth swx ne { /isfixedwidth false def } if }
  483.         ifelse
  484.       } if
  485.  
  486. % Check whether this character must be a subroutine.
  487. % If so, strip off the initial [h]sbw, replace the endchar by a return,
  488. % and put the charstring in the Subrs array.
  489.      subrchars charname known
  490.       { /charstr exch def
  491.         /csindex subrchars charname get def
  492.         charstr parsesbw counttomark 1 add 1 roll
  493.           counttomark 2 eq { 0 exch 0 } if ]
  494.           subrsbw exch csindex exch put
  495.           charstr exch skipsbw /charend exch def pop
  496.         charstr charstr length 1 sub c_return put
  497.         subrs   csindex
  498.           charstr   charend lenIV sub   dup charstr length exch sub
  499.             getinterval copystring
  500.         put
  501.         charstr 0 charend getinterval
  502.           () subrchars charname get concatcall s_endchar concatstrings
  503.           concatstrings
  504.         /subrcount subrcount 1 add def
  505.       }
  506.       { copystring }
  507.      ifelse
  508.      charname exch charstrings 3 1 roll put
  509.        } for
  510.       (ENDFONT) getline
  511.  
  512. %  Synthesize missing characters out of available ones.
  513. %  For fixed-width fonts, only do this in the 1-for-1 case.
  514.       composites
  515.        { 1 index charstrings exch known
  516.           { pop pop }
  517.       { dup isfixedwidth
  518.          { dup length 1 eq }
  519.          { true }
  520.         ifelse
  521.         exch { charstrings exch known and } forall
  522.          { ( /) print 1 index bits cvs print
  523.            dup length 1 eq
  524.             { 0 get charstrings exch get copystring }
  525.         { % Top of stack is array of characters to combine.
  526.           % Convert to an array of subr indices.
  527.           [ exch { subrchars exch get } forall ]
  528.           % The final width is the sum of the widths of all
  529.           % the characters, minus the side bearings of all the
  530.           % characters except the first.  After each character
  531.           % except the last, do a setcurrentpoint of its width
  532.           % minus its side bearing (except for the first character);
  533.           % before each character except the first, do a 0 hmoveto.
  534.           % Fortunately, all this information is available in subrsbw.
  535.           /combine exch def
  536.           lenIV string
  537.           % Compute the total width.
  538.           subrsbw combine 0 get get aload pop pop pop 2 copy
  539.           combine
  540.            { subrsbw exch get
  541.              aload pop ptexch ptsub ptadd
  542.            } forall
  543.           % Encode the combined side bearing and width.
  544.           dup 3 index or 0 eq
  545.            { pop exch pop 2 array astore s_hsbw }
  546.            { 4 array astore s_sbw }
  547.           ifelse
  548.           3 1 roll { concatnum } forall exch concatstrings
  549.           % Encode the subroutine calls, except the last.
  550.           subrsbw combine 0 get get aload pop ptexch pop pop
  551.           0 1 combine length 2 sub
  552.            { combine exch get /ccsi exch def
  553.              2 copy 5 -1 roll ccsi concatcall
  554.              3 -1 roll concatnum exch concatnum
  555.              s_setcurrentpoint_hmoveto concatstrings
  556.              subrsbw ccsi get aload pop ptexch ptsub
  557.              5 -2 roll ptadd
  558.            } for
  559.           % Encode the last call.
  560.           pop pop
  561.           combine dup length 1 sub get concatcall
  562.           s_endchar concatstrings
  563.         } ifelse
  564.            charstrings 3 1 roll put
  565.          }
  566.          { pop pop }
  567.         ifelse
  568.       }
  569.      ifelse
  570.        }
  571.       forall flush
  572.  
  573. %  Synthesize accented characters with seac if needed and possible.
  574.       accentedchars
  575.        { aload pop /accent exch def /base exch def
  576.          buffer cvs /accented exch def
  577.      charstrings accented known not
  578.      charstrings base known and
  579.      charstrings accent known and
  580.       { ( /) print accented print
  581.         charstrings base get findsbw 0 exch getinterval
  582.         /acstring exch def        % start with sbw of base
  583.         charstrings accent get parsesbw
  584.         counttomark 1 sub { pop } repeat    % just leave mark & sbx
  585.         acstring exch concatnum exch pop    % pop the mark
  586.         0 concatnum 0 concatnum        % adx ady
  587.         decoding base get concatnum        % bchar
  588.         decoding accent get concatnum    % achar
  589.         s_seac concatstrings
  590.         charstrings exch accented copystring exch put
  591.       } if
  592.        } forall
  593.  
  594. %  Make a CharStrings entry for .notdef.
  595.       outline lenIV <8b8b0d0e> putinterval    % 0 0 hsbw endchar
  596.       charstrings /.notdef outline 0 lenIV 4 add getinterval copystring put
  597.  
  598. %  Encrypt the CharStrings and Subrs (in place).
  599.       charstrings dup begin
  600.        { 4330 exch dup type1encrypt exch pop
  601.          readonly def
  602.        }
  603.       forall end
  604.       0 1 subrs length 1 sub
  605.        { dup subrs exch get dup null ne
  606.       { 4330 exch dup type1encrypt exch pop
  607.         subrs 3 1 roll put
  608.       }
  609.       { pop pop }
  610.      ifelse
  611.        }
  612.       for
  613.  
  614. %  Make most of the remaining entries in the font dictionaries.
  615.       Font /CharStrings charstrings readonly put
  616.       FontInfo /FullName known not
  617.        { % Some programs insist on FullName being present.
  618.          FontInfo /FullName FontName dup length string cvs put
  619.        }
  620.       if
  621.       FontInfo /isFixedPitch isfixedwidth put
  622.       subrcount 0 gt
  623.        { Private /Subrs subrs readonly put
  624.        } if
  625.  
  626. %  Determine the italic angle and underline position
  627. %  by actually installing the font.
  628.       save
  629.       /_temp_ Font definefont setfont
  630.       [1000 0 0 1000 0 0] setmatrix        % mitigate rounding problems
  631. % The italic angle is the multiple of -5 degrees
  632. % that minimizes the width of the 'I'.
  633.       0 9999 0 5 85
  634.        { dup rotate
  635.          newpath 0 0 moveto (I) false charpath
  636.      dup neg rotate
  637.          pathbbox pop exch pop exch sub
  638.      dup 3 index lt { 4 -2 roll } if
  639.      pop pop
  640.        }
  641.       for pop
  642. % The underline position is halfway between the bottom of the 'A'
  643. % and the bottom of the FontBBox.
  644.       newpath 0 0 moveto (A) false charpath
  645.       FontMatrix concat
  646.       pathbbox pop pop exch pop
  647. %  Put the values in FontInfo.
  648.       3 -1 roll restore
  649.       Font /FontBBox get 1 get add 2 div cvi
  650.       dup FontInfo /UnderlinePosition 3 -1 roll put
  651.       2 div abs FontInfo /UnderlineThickness 3 -1 roll put
  652.       FontInfo /ItalicAngle 3 -1 roll put
  653.  
  654. %  Clean up and finish.
  655.       grestore
  656.       bdfile closefile
  657.       Font currentdict end end begin        % remove font from dict stack
  658.       (\n) print flush
  659.  
  660.     } bind def
  661.  
  662. % ------ Reader for AFM files ------ %
  663.  
  664. % Dictionary for looking up character keywords
  665.    /cmdict 6 dict dup begin
  666.       /C { /c iarg def } def
  667.       /N { /n warg copystring def } def
  668.       /WX { /w narg def } def
  669.       /W0X /WX load def
  670.       /W /WX load def
  671.       /W0 /WX load def
  672.    end def
  673.  
  674.    /readAFM        % fontdict afmfilename readAFM -> fontdict
  675.     { (r) file /bdfile exch def
  676.       /Font exch def
  677.       /commentword (Comment) def
  678.  
  679. %  Check for the StartFontMetrics.
  680.       (StartFontMetrics) getline
  681.       args cvr 2.0 lt { (Not version 2.0 or greater\n) print stop } if
  682.  
  683. %  Look for StartCharMetrics, then parse the character metrics.
  684. %  The only information we care about is the X width.
  685.       /metrics 0 dict def
  686.        { nextline
  687.          keyword (EndFontMetrics) eq { exit } if
  688.      keyword (StartCharMetrics) eq
  689.       { iarg dup dict /metrics exch def
  690.          { /c -1 def /n null def /w null def
  691.            nextline buffer
  692.             { token not { exit } if
  693.           dup cmdict exch known
  694.            { exch /args exch def   cmdict exch get exec   args }
  695.            { pop }
  696.           ifelse
  697.         } loop
  698.            c 0 ge n null ne or w null ne and
  699.             { n null eq { /n Font /Encoding get c get def } if
  700.           metrics n w put
  701.         }
  702.            if
  703.          }
  704.         repeat
  705.         (EndCharMetrics) getline
  706.       } if
  707.        } loop
  708.  
  709. %  Insert the metrics in the font.
  710.        metrics length 0 ne
  711.         { Font /Metrics metrics readonly put
  712.     } if
  713.       Font
  714.     } bind def
  715.  
  716. end        % envBDF
  717.  
  718. % Enter the main program in the current dictionary.
  719. /bdftops
  720.  { [] exch bdfafmtops
  721.  } bind def
  722. /bdfafmtops        % infilename afmfilename* outfilename fontname
  723.             %   encoding uniqueID
  724.  { envBDF begin
  725.      6 -2 roll exch 6 2 roll    % afm* in out fontname encoding uniqueID
  726.      readBDF        % afm* font
  727.      exch { readAFM } forall
  728.      save exch
  729.      dup /FontName get exch definefont
  730.      setfont
  731.      psfile writefont
  732.      restore
  733.      psfile closefile
  734.    end
  735.  } bind def
  736.  
  737. % If the program was invoked from the command line, run it now.
  738. [ shellarguments
  739.  { counttomark 4 ge
  740.     { dup 0 get
  741.       dup 48 ge exch 57 le and        % last arg starts with a digit?
  742.        { cvi /StandardEncoding }        % no encodingname
  743.        { cvn exch cvi exch }            % have encodingname
  744.       ifelse
  745.       counttomark 4 roll
  746.       counttomark 5 sub array astore
  747.       6 -4 roll exch
  748.       bdfafmtops
  749.     }
  750.     { cleartomark
  751.       (Usage: bdftops xx.bdf [yy1.afm ...] zz.gsf fontname uniqueID [encodingname]\n) print flush
  752.       mark
  753.     }
  754.    ifelse
  755.  }
  756. if pop
  757.